package ssau.knyazev.fft.modules.threads;

import java.util.concurrent.CyclicBarrier;

import ssau.knyazev.core.CComplex;

public class CComplexMirrorThread implements Runnable {

	protected int idx;
	protected int idy;
	protected int uid;
	protected int blockX;
	protected int blockY;
	private CComplex[][] data = null;
	private CyclicBarrier barrier = null;
	private CComplex[] wilist = null;
	private CComplex[] wjlist = null;
	
	
	public CComplexMirrorThread(CyclicBarrier barrier, int idx, int idy, int blockX, int blockY, CComplex[][] data, CComplex[] wilist, CComplex[] wjlist){
		this.idx = idx;
		this.idy = idy;
		this.uid = idy*blockX + idx;
		this.data = data;
		this.blockX = blockX;
		this.blockY = blockY;
		this.wilist = wilist;
		this.wjlist = wjlist;
		this.barrier = barrier;
	}
	
	public void run() {
		int N = data.length;
		int size = 0;
		for (int x = data.length; x > 1; x = x >> 1){
			size++;
		}
		int step, matrixWidth;
		boolean flag;
		for (int q = 0; q < size; q++) {
			step = (int)Math.pow(2, q);
			matrixWidth = step*2;
			int part = matrixWidth/4;
			for (int down = idx; down < N - step; down = down + blockX) {
				for (int right = idy; right < N - step; right = right + blockY) {
					int dmatrInd = down/matrixWidth * matrixWidth;
					int rmatrInd = right/matrixWidth * matrixWidth;
					int tdown = down - dmatrInd;
					int tright = right - rmatrInd;
					flag = (tdown <= part && tright <= part);
					if (flag) {
						CComplex t1 = wilist[(down % matrixWidth)*(N/matrixWidth)];
						CComplex t2 = wjlist[(right % matrixWidth)*(N/matrixWidth)];
						butterfly(data[down][right], data[down][right + step], data[down + step][right], data[down + step][right + step], t1, t2);
						
						if (q >= 2){
							int rmirr = mirrorIndex(right, matrixWidth);//(rmatrInd == right) ? right : matrixWidth - right + 2*rmatrInd;
							int dmirr = mirrorIndex(down, matrixWidth); // (dmatrInd == down) ? down : matrixWidth - down + 2*dmatrInd;
							
							mirror(data, down, right, dmirr, rmirr);
							
							rmirr = mirrorIndex(right + step, matrixWidth);
							mirror(data, down, right + step, dmirr, rmirr);
							
							dmirr = mirrorIndex(down + step, matrixWidth);
							rmirr = mirrorIndex(right, matrixWidth);
							mirror(data, down + step, right, dmirr, rmirr);
							
							rmirr = mirrorIndex(right + step, matrixWidth);
							mirror(data, down + step, right + step, dmirr, rmirr);
						}

					}
				}
			}
			try{
				barrier.await();
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}
	
	protected static void butterfly(CComplex a00, CComplex a01, CComplex a10, CComplex a11, CComplex wi, CComplex wj) {
		CComplex newa00 = a00.clone();
		CComplex newa10 = a00.clone();
		CComplex newa01 = a00.clone();
		CComplex newa11 = a00.clone();
		
		CComplex t = wi.multy(a10);
		newa00 = newa00.add(t);
		newa10 = newa10.sub(t);
		newa01 = newa01.add(t);
		newa11 = newa11.sub(t);
		
		t = wj.multy(a01);
		newa00 = newa00.add(t);
		newa10 = newa10.add(t);
		newa01 = newa01.sub(t);
		newa11 = newa11.sub(t);
		
		t = wj.multy(a11);
		t = wi.multy(t);
		newa00 = newa00.add(t);
		newa10 = newa10.sub(t);
		newa01 = newa01.sub(t);
		newa11 = newa11.add(t);

		copyValues(a00, newa00);
		copyValues(a01, newa01);
		copyValues(a10, newa10);
		copyValues(a11, newa11);
	}

	protected static void copyValues(CComplex a, CComplex b) {
		a.setA(b.getA());
		a.setB(b.getB());
		a.setC(b.getC());
		a.setD(b.getD());
	}
	
	protected static int mirrorIndex(int srcIndex, int matrixWidth){
		int matrixIndex = srcIndex/matrixWidth * matrixWidth;
		return (matrixIndex == srcIndex) ? srcIndex : matrixWidth - srcIndex + 2*matrixIndex;
	}
	
	protected static void mirror(CComplex[][] src, int down, int right, int dmirr, int rmirr){
		if (rmirr != right){
			src[down][rmirr].setA(src[down][right].getA());
			src[down][rmirr].setB(src[down][right].getB());
			src[down][rmirr].setC(-src[down][right].getC());
			src[down][rmirr].setD(-src[down][right].getD());
		}
		if (dmirr != down){
			src[dmirr][right].setA(src[down][right].getA());
			src[dmirr][right].setB(-src[down][right].getB());
			src[dmirr][right].setC(src[down][right].getC());
			src[dmirr][right].setD(-src[down][right].getD());
		}
		if (rmirr != right && dmirr != down){
			src[dmirr][rmirr].setA(src[down][right].getA());
			src[dmirr][rmirr].setB(-src[down][right].getB());
			src[dmirr][rmirr].setC(-src[down][right].getC());
			src[dmirr][rmirr].setD(src[down][right].getD());
		}
	}
	
}
